package org.flowcircuit.model.board


import appearance.{Style, DrawContext}
import component.Component
import coordinates.{Dim, Pos}
import java.awt.Graphics2D
import utils.ParameterChecker._
import utils.{CallbackList, Property}


/**
 * Something components can be attached to.
 * Has sockets, and some pattern of connections between them.
 * 
 * @author Hans Haggstrom
 */
// TODO: Add silkscreen?  or just use comment components?
case class Board() extends CircuitElement {

  val components = CallbackList[Component]( onElementAdded _, onElementRemoved _ )
  val sockets    = CallbackList[Socket]   ( onElementAdded _, onElementRemoved _ )
  val junctions  = CallbackList[Junction] ( onElementAdded _, onElementRemoved _ )
  val traces     = CallbackList[Trace]    ( onElementAdded _, onElementRemoved _ )

  val boardStyle = Property( Style(), () => markRedrawNeeded() )
  val dimension = Property( Dim(100,100), () => markRedrawNeeded() )

  def getElementAtPosition( queryPos : Pos, context : DrawContext  ) : Option[CircuitElement] = {

    def findFirstOverlap( elementLists : List[CircuitElement] * ) : Option[CircuitElement] = {
      elementLists.foreach{ elementList : List[CircuitElement] =>
        elementList.find( _.isInside( queryPos, context ) ) match {
          case Some(x) => return Some(x)
          case None => // fallthrough to next list
        }
      }
      
      None
    }

    findFirstOverlap( components, sockets, junctions, traces, List(this) )
  }

  def renderBoard( context : DrawContext ) {
    context.rectangle( pos, dimension, boardStyle )
  }

  def isInside(queryPos: Pos, context: DrawContext) = {
    context.isInsideRectangle( queryPos, pos, dimension, boardStyle )
  }

  def render( context : DrawContext ) {

    val projectedContext = context.project( posDir )

    renderBoard( projectedContext )

    traces foreach { _.render( projectedContext ) }
    junctions foreach { _.render( projectedContext ) }
    sockets foreach { _.render( projectedContext ) }
    getComponentsInHeightOrder foreach { _.render(projectedContext) }
  }

  def getComponentsInHeightOrder : List[Component] = {
    // TODO: Create a class that stores the components in a sorted list all the time instead, to optimize drawing speed for (very) large number of components.
    components.sort( (c1 : Component, c2:Component)  => c1.height < c2.height )
  }

  private def onElementAdded( e : CircuitElement ) {
    e.board() = this
    markRedrawNeeded()
  }

  private def onElementRemoved( e : CircuitElement ) {
    e.board() = null
    markRedrawNeeded()
  }


}

                                             